#pragma once

#include <list>
#include <mutex>
#include <condition_variable>

template<typename T>
class BlockQueue
{
public:
	BlockQueue(int size = 5)
		: size_(size)
		, quit_(false)
	{}
	~BlockQueue(){}
		
	bool front(T& data)
	{
		if(quit_) return false;

		std::unique_lock<std::mutex> lck(mutex_);

		pop_cond_.wait(lck,[this](){
			return list_.size() > 0 || quit_;
		});

		if(quit_) return false;

		data = list_.front();
		list_.pop_front();
		
		push_cond_.notify_one();

		return true;
	}

	void push_back(const T& data)
	{
		if(quit_) return;

		std::unique_lock<std::mutex> lck(mutex_);

		push_cond_.wait(lck,[this](){
			return list_.size() < size_ || quit_;
		});
		if(quit_) return;

		list_.emplace_back(data);

		pop_cond_.notify_one();
	}

	int size()
	{
		std::lock_guard<std::mutex> lck(mutex_);
		return list_.size();
	}

	bool empty()
	{
		std::lock_guard<std::mutex> lck(mutex_);
		return list_.empty();
	}

	void clear()
	{
		std::lock_guard<std::mutex> lck(mutex_);
		list_.clear();
	}

	void quit()
	{
		quit_ = true;
		pop_cond_.notify_all();
		push_cond_.notify_all();
	}

	template<typename CALLABLE>
	void clear_foreach(const CALLABLE& cb)
	{
		std::lock_guard<std::mutex> lck(mutex_);
		for(auto it = list_.begin(); it != list_.end();)
		{
			if (cb) cb(*it);
			it = list_.erase(it);
		}
	}
private:
	bool					quit_;
	int						size_;
	std::list<T>			list_;
	std::mutex				mutex_;
	std::condition_variable push_cond_;
	std::condition_variable pop_cond_;
};

